package com.fire.threadpool.interceptLongTimeThread;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;

/**
 * TimeRecordThreadPoolExecutor
 *
 * @author zwd
 * @date 2022/3/23
 * @desc
 */
public class TimeOverInterceptThreadPoolExecutor extends ThreadPoolExecutor {
	private final Logger log = Logger.getLogger("TimeOverInterceptThreadPoolExecutor");
	private final ThreadLocal<Long> startTime = new ThreadLocal<>();
	private final AtomicLong numTasks = new AtomicLong();
	private final AtomicLong totalTime = new AtomicLong();
	private DelayQueue<DelayTaskThread> delayQueue = new DelayQueue<>();
	;
	private final ReentrantLock r = new ReentrantLock();

	public void init() {
		new Thread(() -> {
			for (; ; ) {
				r.lock();
				DelayTaskThread delayTaskThread = null;
				try {
					delayTaskThread = delayQueue.take();
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}
				if (delayTaskThread != null && delayTaskThread.getCurrentThread() != null) {
					log.info(String
							.format("Thread %s has bean take form delayQueue, it will be interrupt", delayTaskThread
									.getCurrentThread()));
					delayTaskThread.getCurrentThread().interrupt();
				}

				r.unlock();
			}
		}).start();
	}

	public TimeOverInterceptThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
		this.init();
	}

	public TimeOverInterceptThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
		this.init();
	}

	public TimeOverInterceptThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
		this.init();
	}

	public TimeOverInterceptThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
		this.init();
	}

	@Override
	protected void beforeExecute(Thread t, Runnable r) {
		super.beforeExecute(t, r);
		log.info(String.format("Thread %s: start %s", t, r));
		startTime.set(System.nanoTime());
	}

	@Override
	public void execute(Runnable command) {
		super.execute(command);
		System.out.println("begin to execute");
		DelayTaskThread delayTaskThread = (DelayTaskThread) command;
		delayQueue.add(delayTaskThread);
		log.info(String.format("Thread %s has been add tp delay queue", delayTaskThread.getCurrentThread()));
	}

	@Override
	protected void afterExecute(Runnable r, Throwable t) {
		try {
			long endTime = System.nanoTime();
			long taskTime = endTime - startTime.get();
			numTasks.incrementAndGet();
			totalTime.addAndGet(taskTime);
			log.info(String.format("Thread %s: end %s, time=%dns", t, r, taskTime));
		}
		finally {
			DelayTaskThread delayTaskThread = (DelayTaskThread) r;
			delayQueue.remove(delayTaskThread);
			log.info(String.format("Thread %s has been remove from delay queue", delayTaskThread.getCurrentThread()));
			super.afterExecute(r, t);
		}
	}

	@Override
	protected void terminated() {
		try {
			log.info(String.format("Terminated: avg time=%dns", totalTime.get() / numTasks.get()));
		}
		finally {
			super.terminated();
		}
	}
}
